
(function() {

	/**
	 * @namespace Utils
	 * @class TreeNode
	 */
	TreeNode = G.GClass('Utils', 'TreeNode');

	/**
	 * Constructure of TreeNode
	 * @method __init
	 */
	TreeNode.prototype.__init = function(id, data) 
	{
		this.data = data;
		this.id = id;

		this.childNodes = {};
		this.childCount = 0;
		this.path = [id];
		this.father = null;
	}

	TreeNode.prototype.toString = function() { return this.id; }

	/**
	 * Dump the whole tree to a string to list of all node's full path.
	 * @method dump
	 */
	TreeNode.prototype.dump = function() {
		var str;
		str = this.get_fullname();
		for (var n  in this.childNodes) {
			str += ", " + this.childNodes[n].dump();
		}
		return str;
	}


	/**
	 * Get deep in tree.
	 * @method get_deep
	 * @return {integer} the deep of the node in tree. The root node has a deep of 0.
	 */
	TreeNode.prototype.get_deep = function() {
		return this.path.length - 1;
	}

	/**
	 * Get child node amount.
	 * @method get_child_count
	 * @return {integer}
	 */
	TreeNode.prototype.get_child_count = function() {
		return this.childCount;
	}

	/**
	 * Get the father node.
	 * @method get_father
	 * @return {TreeNode} 
	 */
	TreeNode.prototype.get_father = function() {
		return this.father;
	}

	/**
	 * Get the fullname to represet the position in tree.
	 * @method get_fullname 
	 * @return {string} 
	 */
	TreeNode.prototype.get_fullname = function() {
		return this.path.join('/');
	}

	/**
	 * Add a child node.
	 * @method append_child
	 */
	TreeNode.prototype.append_child = function(node) 
	{
		var id = node.id;
		if (!this.childNodes.propertyIsEnumerable(id)) {
			this.childNodes[id] = node;
			this.childCount += 1;
			node.__set_path(this.path);
			node.father = this;
		}
	}


	/**
	 * Remove child node
	 * @method remove_child
	 */
	TreeNode.prototype.remove_child = function(sub) {
		// Todo...
	}

	/**
	 * Search the node with the given full path
	 *
	 * @method search
	 * @param fullname {string}
	 *
	 */
	TreeNode.prototype.search = function(fullname) {
		var arr = fullname.split('/');
		var t = this;
		for (n in arr) {
			var id = arr[n];
			if (this.childNodes.propertyIsEnumerable(id)) {
				t = t.childNodes[id];
			} else {
				return null;
			}
		}
		return t;
	}

	/**
	 * Disconnect the node from the tree. The path node and all of it's child nodes
	 * will be updated.
	 * @function disconnect
	 */
	TreeNode.prototype.disconnect = function() {
		if (this.father == null)	return;
		
		var id = this.id;
		delete this.father.childNodes[id];
		this.father.childCount -= 1;
		var i = this.father.path.length;
		this.__bubble_up(i);

		this.father = null;
	}

	/**
	 * Change the position of the nodeDisconnect the node from it's father and 
	 * @function move
	 * @param father {TreeNode} The new father of the node will be conected to.
	 */
	TreeNode.prototype.move = function(father) {

		if (father.search("" + this.id) != null)	{
			return false;
		}
		if (this.father) {
			this.disconnect();
		}
		father.append_child(this);
	}


	/*
	 * Decrease the depth of path
	 */
	TreeNode.prototype.__bubble_up = function(lvl) {
		var len = this.path.length - 1;

		if (len >= lvl) {
			this.path.splice(0, lvl);
		} else {
			this.path = [this.id];
		}
		for (var n in this.childNodes) {
			this.childNodes[n].__bubble_up(lvl);
		}
	}


	TreeNode.prototype.__set_path = function(path) {
		this.path = path.concat(this.path);
		for (n in this.childNodes) {
			this.childNodes[n].__set_path(path);
		}
	}


	Tree = G.GClass('Utils', 'Tree');
	Tree.prototype.__init = function(nodeType, subs) 
	{
		this._nodeType = nodeType;
		if (typeof(subs) == 'string') {
			this.get_subs = function(data) {return data[subs]};
		} else if (typeof(subs) == 'function') {
			this.get_subs = subs
		} else {
			this.get_subs = function(data) {return data.subs};
		}
	}

	Tree.prototype.create_node = function(data) 
	{
		return new this._nodeType(data);
	}

	Tree.prototype.seek_in_subs = function(data, indx) 
	{ 
		this._subIndex = indx || 0;
	}

	Tree.prototype.step_in_subs = function(data) 
	{
		if (this._subIndex >= this.data.subs.length) {
			return null;
		}
		return this.data.subs[this._subIndex++];
	}


	Tree.prototype.set_root = function(root)
	{
		this.root = root;
	}

	Tree.prototype.import_tree_data = function(data, root) 
	{
		var root = root || null;

		var node = this.create_node(data);
		if (root == null) {
			this.root = node;
		} else {
			root.append_child(node);
		}

		this.seek_in_subs(data, 0);
		for (s = this.step_in_subs(); s != null; s = this.step_in_subs()) {
			this.import_tree_data(s, node);
		}
	}
})();

